﻿
namespace Scoreboard.Model.Services
{
	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.ComponentModel.DataAnnotations;
	using System.Data;
	using System.Linq;
	using System.ServiceModel.DomainServices.EntityFramework;
	using System.ServiceModel.DomainServices.Hosting;
	using System.ServiceModel.DomainServices.Server;
	using Scoreboard.Model;
	using System.ServiceModel;
using Scoreboard.Model.Authentication;


	// Implements application logic using the EntitiesContainer context.
	// TODO: Add your application logic to these methods or in additional methods.
	// TODO: Wire up authentication (Windows/ASP.NET Forms) and uncomment the following to disable anonymous access
	// Also consider adding roles to restrict access as appropriate.
	//[RequiresAuthentication]
	[ServiceBehavior(IncludeExceptionDetailInFaults = false, AddressFilterMode = AddressFilterMode.Any)]
	[EnableClientAccess]
	[RequiresAuthentication]
	public class ScoreboardDomainService : LinqToEntitiesDomainService<EntitiesContainer>
	{
		private ScoreboardUser _user;

		public ScoreboardDomainService(ScoreboardUser user)
		{	
			_user = user;
		}

		[Invoke]
		public void EnterRatingResult(User player, User playerMate, User opponent, User opponentMate,
									double participantScore, double opponentScore)
		{
			if (participantScore == opponentScore
				|| player == null
				|| opponent == null
				|| GetUser(player) == GetUser(opponent)
				|| (playerMate == null && opponentMate != null)
				|| (playerMate != null && opponentMate == null)
				|| (playerMate != null && (
					GetUser(player) == GetUser(playerMate)
					|| GetUser(player) == GetUser(opponentMate)
					|| GetUser(opponent) == GetUser(playerMate)
					|| GetUser(opponent) == GetUser(opponentMate))))
			{
				return;
			}

			var game = new Game
				{
					Participant = new Participant { Player = GetUser(player), Mate = GetUser(playerMate) },
					Opponent = new Participant { Player = GetUser(opponent), Mate = GetUser(opponentMate) },
					ParticipantScore = participantScore,
					OpponentScore = opponentScore,
					Date = DateTime.Now
				};

			RatingEvaluator.ApplyOnRating(game);
			ObjectContext.Games.AddObject(game);

			ObjectContext.SaveChanges();
		}

		[Invoke]
		public void EnterTournamentResult(User player, User playerMate, User opponent, User opponentMate,
									double participantScore, double opponentScore, int groupId)
		{
			if (participantScore == opponentScore
				|| player == null
				|| opponent == null
				|| GetUser(player) == GetUser(opponent)
				|| (playerMate == null && opponentMate != null)
				|| (playerMate != null && opponentMate == null)
				|| (playerMate != null && (
					GetUser(player) == GetUser(playerMate)
					|| GetUser(player) == GetUser(opponentMate)
					|| GetUser(opponent) == GetUser(playerMate)
					|| GetUser(opponent) == GetUser(opponentMate))))
			{
				return;
			}

			var group = ObjectContext.Groups.Where(g => g.Id == groupId).SingleOrDefault();
			if (group == null)
			{
				return;
			}

			var game = new Game
			{
				Participant = new Participant { Player = GetUser(player), Mate = GetUser(playerMate) },
				Opponent = new Participant { Player = GetUser(opponent), Mate = GetUser(opponentMate) },
				ParticipantScore = participantScore,
				OpponentScore = opponentScore,
				Date = DateTime.Now
			};

			group.Games.Add(game);

			RatingEvaluator.ApplyOnRating(game);			

			ObjectContext.SaveChanges();
		}

		private User GetUser(User user)
		{
			if (user == null)
			{
				return null;
			}

			return ObjectContext.Users.Where(u => u.Login == user.Login).Single();
		}

		// TODO:
		// Consider constraining the results of your query method.  If you need additional input you can
		// add parameters to this method or create additional query methods with different names.
		// To support paging you will need to add ordering to the 'Games' query.
		public IQueryable<Game> GetRatingGames()
		{
			return this.ObjectContext.Games
				.Include("Participant")
				.Include("Participant.Player")
				.Include("Participant.Mate")
				.Include("Opponent")
				.Include("Opponent.Player")
				.Include("Opponent.Mate")
				.Where(g => g.GroupId == null)
				.OrderByDescending(g => g.Date)
				.Take(39);
		}

		[RequiresRole("Manager")]
		public void InsertGame(Game game)
		{
			if ((game.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(game, EntityState.Added);
			}
			else
			{
				this.ObjectContext.Games.AddObject(game);
			}
		}

		[RequiresRole("Manager")]
		public void UpdateGame(Game currentGame)
		{
			this.ObjectContext.Games.AttachAsModified(currentGame, this.ChangeSet.GetOriginal(currentGame));
		}

		[RequiresRole("Manager")]
		public void DeleteGame(Game game)
		{
			if ((game.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(game, EntityState.Deleted);
			}
			else
			{
				this.ObjectContext.Games.Attach(game);
				this.ObjectContext.Games.DeleteObject(game);
			}
		}

		// TODO:
		// Consider constraining the results of your query method.  If you need additional input you can
		// add parameters to this method or create additional query methods with different names.
		// To support paging you will need to add ordering to the 'Groups' query.
		public IQueryable<Group> GetGroups()
		{
			return this.ObjectContext.Groups;
		}

		[RequiresRole("Manager")]
		public void InsertGroup(Group group)
		{
			if ((group.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(group, EntityState.Added);
			}
			else
			{
				this.ObjectContext.Groups.AddObject(group);
			}
		}

		[RequiresRole("Manager")]
		public void UpdateGroup(Group currentGroup)
		{
			this.ObjectContext.Groups.AttachAsModified(currentGroup, this.ChangeSet.GetOriginal(currentGroup));
		}

		[RequiresRole("Manager")]
		public void DeleteGroup(Group group)
		{
			if ((group.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(group, EntityState.Deleted);
			}
			else
			{
				this.ObjectContext.Groups.Attach(group);
				this.ObjectContext.Groups.DeleteObject(group);
			}
		}

		// TODO:
		// Consider constraining the results of your query method.  If you need additional input you can
		// add parameters to this method or create additional query methods with different names.
		// To support paging you will need to add ordering to the 'Participants' query.
		public IQueryable<Participant> GetParticipants()
		{
			return this.ObjectContext.Participants;
		}

		[RequiresRole("Manager")]
		public void InsertParticipant(Participant participant)
		{
			if ((participant.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(participant, EntityState.Added);
			}
			else
			{
				this.ObjectContext.Participants.AddObject(participant);
			}
		}

		[RequiresRole("Manager")]
		public void UpdateParticipant(Participant currentParticipant)
		{
			this.ObjectContext.Participants.AttachAsModified(currentParticipant, this.ChangeSet.GetOriginal(currentParticipant));
		}

		[RequiresRole("Manager")]
		public void DeleteParticipant(Participant participant)
		{
			if ((participant.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(participant, EntityState.Deleted);
			}
			else
			{
				this.ObjectContext.Participants.Attach(participant);
				this.ObjectContext.Participants.DeleteObject(participant);
			}
		}

		// TODO:
		// Consider constraining the results of your query method.  If you need additional input you can
		// add parameters to this method or create additional query methods with different names.
		// To support paging you will need to add ordering to the 'News' query.
		public IQueryable<PeaceOfNews> GetNews()
		{
			return this.ObjectContext.News;
		}

		[RequiresRole("Manager")]
		public void InsertPeaceOfNews(PeaceOfNews peaceOfNews)
		{
			if ((peaceOfNews.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(peaceOfNews, EntityState.Added);
			}
			else
			{
				this.ObjectContext.News.AddObject(peaceOfNews);
			}
		}

		[RequiresRole("Manager")]
		public void UpdatePeaceOfNews(PeaceOfNews currentPeaceOfNews)
		{
			this.ObjectContext.News.AttachAsModified(currentPeaceOfNews, this.ChangeSet.GetOriginal(currentPeaceOfNews));
		}

		[RequiresRole("Manager")]
		public void DeletePeaceOfNews(PeaceOfNews peaceOfNews)
		{
			if ((peaceOfNews.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(peaceOfNews, EntityState.Deleted);
			}
			else
			{
				this.ObjectContext.News.Attach(peaceOfNews);
				this.ObjectContext.News.DeleteObject(peaceOfNews);
			}
		}

		// TODO:
		// Consider constraining the results of your query method.  If you need additional input you can
		// add parameters to this method or create additional query methods with different names.
		// To support paging you will need to add ordering to the 'Playgrounds' query.
		public IQueryable<Playground> GetPlaygrounds()
		{
			return this.ObjectContext.Playgrounds;
		}

		[RequiresRole("Manager")]
		public void InsertPlayground(Playground playground)
		{
			if ((playground.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(playground, EntityState.Added);
			}
			else
			{
				this.ObjectContext.Playgrounds.AddObject(playground);
			}
		}

		[RequiresRole("Manager")]
		public void UpdatePlayground(Playground currentPlayground)
		{
			this.ObjectContext.Playgrounds.AttachAsModified(currentPlayground, this.ChangeSet.GetOriginal(currentPlayground));
		}

		[RequiresRole("Manager")]
		public void DeletePlayground(Playground playground)
		{
			if ((playground.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(playground, EntityState.Deleted);
			}
			else
			{
				this.ObjectContext.Playgrounds.Attach(playground);
				this.ObjectContext.Playgrounds.DeleteObject(playground);
			}
		}

		// TODO:
		// Consider constraining the results of your query method.  If you need additional input you can
		// add parameters to this method or create additional query methods with different names.
		// To support paging you will need to add ordering to the 'PlaygroundLeases' query.
		public IQueryable<PlaygroundLease> GetPlaygroundLeases()
		{
			return this.ObjectContext.PlaygroundLeases;
		}

		[RequiresRole("Manager")]
		public void InsertPlaygroundLease(PlaygroundLease playgroundLease)
		{
			if ((playgroundLease.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(playgroundLease, EntityState.Added);
			}
			else
			{
				this.ObjectContext.PlaygroundLeases.AddObject(playgroundLease);
			}
		}

		[RequiresRole("Manager")]
		public void UpdatePlaygroundLease(PlaygroundLease currentPlaygroundLease)
		{
			this.ObjectContext.PlaygroundLeases.AttachAsModified(currentPlaygroundLease, this.ChangeSet.GetOriginal(currentPlaygroundLease));
		}

		[RequiresRole("Manager")]
		public void DeletePlaygroundLease(PlaygroundLease playgroundLease)
		{
			if ((playgroundLease.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(playgroundLease, EntityState.Deleted);
			}
			else
			{
				this.ObjectContext.PlaygroundLeases.Attach(playgroundLease);
				this.ObjectContext.PlaygroundLeases.DeleteObject(playgroundLease);
			}
		}

		// TODO:
		// Consider constraining the results of your query method.  If you need additional input you can
		// add parameters to this method or create additional query methods with different names.
		// To support paging you will need to add ordering to the 'Roles' query.
		public IQueryable<Role> GetRoles()
		{
			return this.ObjectContext.Roles;
		}

		[RequiresRole("Manager")]
		public void InsertRole(Role role)
		{
			if ((role.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(role, EntityState.Added);
			}
			else
			{
				this.ObjectContext.Roles.AddObject(role);
			}
		}

		[RequiresRole("Manager")]
		public void UpdateRole(Role currentRole)
		{
			this.ObjectContext.Roles.AttachAsModified(currentRole, this.ChangeSet.GetOriginal(currentRole));
		}

		[RequiresRole("Manager")]
		public void DeleteRole(Role role)
		{
			if ((role.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(role, EntityState.Deleted);
			}
			else
			{
				this.ObjectContext.Roles.Attach(role);
				this.ObjectContext.Roles.DeleteObject(role);
			}
		}

		// TODO:
		// Consider constraining the results of your query method.  If you need additional input you can
		// add parameters to this method or create additional query methods with different names.
		// To support paging you will need to add ordering to the 'Scripts' query.
		public IQueryable<Script> GetScripts()
		{
			return this.ObjectContext.Scripts;
		}

		[RequiresRole("Manager")]
		public void InsertScript(Script script)
		{
			if ((script.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(script, EntityState.Added);
			}
			else
			{
				this.ObjectContext.Scripts.AddObject(script);
			}
		}

		[RequiresRole("Manager")]
		public void UpdateScript(Script currentScript)
		{
			this.ObjectContext.Scripts.AttachAsModified(currentScript, this.ChangeSet.GetOriginal(currentScript));
		}

		[RequiresRole("Manager")]
		public void DeleteScript(Script script)
		{
			if ((script.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(script, EntityState.Deleted);
			}
			else
			{
				this.ObjectContext.Scripts.Attach(script);
				this.ObjectContext.Scripts.DeleteObject(script);
			}
		}

		// TODO:
		// Consider constraining the results of your query method.  If you need additional input you can
		// add parameters to this method or create additional query methods with different names.
		// To support paging you will need to add ordering to the 'Stages' query.
		public IQueryable<Stage> GetStages()
		{
			return this.ObjectContext.Stages;
		}

		[RequiresRole("Manager")]
		public void InsertStage(Stage stage)
		{
			if ((stage.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(stage, EntityState.Added);
			}
			else
			{
				this.ObjectContext.Stages.AddObject(stage);
			}
		}

		[RequiresRole("Manager")]
		public void UpdateStage(Stage currentStage)
		{
			this.ObjectContext.Stages.AttachAsModified(currentStage, this.ChangeSet.GetOriginal(currentStage));
		}

		[RequiresRole("Manager")]
		public void DeleteStage(Stage stage)
		{
			if ((stage.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(stage, EntityState.Deleted);
			}
			else
			{
				this.ObjectContext.Stages.Attach(stage);
				this.ObjectContext.Stages.DeleteObject(stage);
			}
		}

		// TODO:
		// Consider constraining the results of your query method.  If you need additional input you can
		// add parameters to this method or create additional query methods with different names.
		// To support paging you will need to add ordering to the 'SubTournaments' query.
		public IQueryable<SubTournament> GetSubTournaments()
		{
			return this.ObjectContext.SubTournaments;
		}

		[RequiresRole("Manager")]
		public void InsertSubTournament(SubTournament subTournament)
		{
			if ((subTournament.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(subTournament, EntityState.Added);
			}
			else
			{
				this.ObjectContext.SubTournaments.AddObject(subTournament);
			}
		}

		[RequiresRole("Manager")]
		public void UpdateSubTournament(SubTournament currentSubTournament)
		{
			this.ObjectContext.SubTournaments.AttachAsModified(currentSubTournament, this.ChangeSet.GetOriginal(currentSubTournament));
		}

		[RequiresRole("Manager")]
		public void DeleteSubTournament(SubTournament subTournament)
		{
			if ((subTournament.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(subTournament, EntityState.Deleted);
			}
			else
			{
				this.ObjectContext.SubTournaments.Attach(subTournament);
				this.ObjectContext.SubTournaments.DeleteObject(subTournament);
			}
		}

		// TODO:
		// Consider constraining the results of your query method.  If you need additional input you can
		// add parameters to this method or create additional query methods with different names.
		// To support paging you will need to add ordering to the 'Tournaments' query.			
		public IQueryable<Tournament> GetTournaments()
		{
			return this.ObjectContext.Tournaments
				 .Include("Participants")
				 .Include("Participants.Player")
				 .Include("Participants.Mate")
				 .Include("Stages")
				 .Include("Stages.Groups")
				 .Include("Stages.SubTournaments")
				 .Include("Stages.SubTournaments.Stages")
				 .Include("Stages.SubTournaments.Stages.Groups")
				 .Include("Stages.SubTournaments.Stages.Groups.Games")
				 .Include("Stages.SubTournaments.Stages.Groups.Participants")
				 .Include("Stages.SubTournaments.Stages.Groups.Games.Participant")
				 .Include("Stages.SubTournaments.Stages.Groups.Games.Opponent")
				 .Include("Stages.SubTournaments.Stages.Groups.Games.Participant.Player")
				 .Include("Stages.SubTournaments.Stages.Groups.Games.Participant.Mate")
				 .Include("Stages.SubTournaments.Stages.Groups.Games.Opponent.Player")
				 .Include("Stages.SubTournaments.Stages.Groups.Games.Opponent.Mate")
				 .Include("Stages.Groups.Games")
				 .Include("Stages.Groups.Participants")
				 .Include("Stages.Groups.Games.Participant")
				 .Include("Stages.Groups.Games.Opponent")
				 .Include("Stages.Groups.Games.Participant.Player")
				 .Include("Stages.Groups.Games.Participant.Mate")
				 .Include("Stages.Groups.Games.Opponent.Player")
				 .Include("Stages.Groups.Games.Opponent.Mate")
				 .OrderByDescending(t => t.DateStarted);
		}

		[RequiresRole("Manager")]
		public void InsertTournament(Tournament tournament)
		{
			if ((tournament.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(tournament, EntityState.Added);
			}
			else
			{
				this.ObjectContext.Tournaments.AddObject(tournament);
			}
		}

		[RequiresRole("Manager")]
		public void UpdateTournament(Tournament currentTournament)
		{
			this.ObjectContext.Tournaments.AttachAsModified(currentTournament, this.ChangeSet.GetOriginal(currentTournament));
		}

		[RequiresRole("Manager")]
		public void DeleteTournament(Tournament tournament)
		{
			if ((tournament.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(tournament, EntityState.Deleted);
			}
			else
			{
				this.ObjectContext.Tournaments.Attach(tournament);
				this.ObjectContext.Tournaments.DeleteObject(tournament);
			}
		}

		// TODO:
		// Consider constraining the results of your query method.  If you need additional input you can
		// add parameters to this method or create additional query methods with different names.
		// To support paging you will need to add ordering to the 'Users' query.
		public IQueryable<User> GetUsers()
		{
			return this.ObjectContext.Users;
		}

		public IQueryable<User> GetRatingTable()
		{
			return this.ObjectContext.Users.Where(user => user.RatingGames > 4 && !string.IsNullOrEmpty(user.EmployeeId))
										   .OrderByDescending(user => user.RatingValue);
		}

		[RequiresRole("Manager")]
		public void InsertUser(User user)
		{
			if ((user.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(user, EntityState.Added);
			}
			else
			{
				this.ObjectContext.Users.AddObject(user);
			}
		}

		[RequiresRole("Manager")]
		public void UpdateUser(User currentUser)
		{
			this.ObjectContext.Users.AttachAsModified(currentUser, this.ChangeSet.GetOriginal(currentUser));
		}

		[RequiresRole("Manager")]
		public void DeleteUser(User user)
		{
			if ((user.EntityState != EntityState.Detached))
			{
				this.ObjectContext.ObjectStateManager.ChangeObjectState(user, EntityState.Deleted);
			}
			else
			{
				this.ObjectContext.Users.Attach(user);
				this.ObjectContext.Users.DeleteObject(user);
			}
		}

		public void Register()
		{
			var user = ExternalUserDataContainer.Current.GetUser(_user.Name);
			if (user == null)
			{
				return;
			}

			var container = new EntitiesContainer();
			container.AddToUsers(user);
			container.SaveChanges();
		}
	}
}


